#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "Bloque.h"
#include "FichBits.h"
#include "ImagenES.h"
#include <vector>
using namespace std;



// BUSCA EL MAYOR COEFICIENTE DE UN BLOQUE DE 64 ELEMENTOS
// EN EL EJEMPLO ES 21.
int buscaMayorCoeficiente(Bloque *bloque){

    int maxCoeficiente = 0;
    for (int i = 0; i < bloque->alto; i++){
     for (int j = 0; j < bloque->ancho; j++){
        if(abs(bloque->matrix[i][j]) > maxCoeficiente) maxCoeficiente = abs(bloque->matrix[i][j]);
     }
    }
    cout << "Mayor coeficiente: " << maxCoeficiente << endl;
    return maxCoeficiente;
}

// CALCULA EL NÚMERO DE BITPLANES NECESARIOS
// EN EL EJEMPLO ES 5 (de 4 a 0)
int getNumBitPlanes(Bloque *bloque){
    int maxCoeficiente = buscaMayorCoeficiente(bloque);
    int numBitPlanes = (int) (log2(maxCoeficiente))+1;
    return numBitPlanes;
}

// A PARTIR DE UN COEFICIENTE Y EL BITPLANE EN EL QUE SE
// ENCUENTRA LA EJECUCIÓN SE SABE SI ES SIGNIFICATIVO O NO
bool esSignificativo(int coeficiente, int bitsSignificativos){
    if(coeficiente != 0){
        int bitsCoeficiente = log2(abs(coeficiente))+1;
        return (bitsCoeficiente > bitsSignificativos) ;
    }
    else return 0;
}

void copiarMatriz(bool **destino, bool **original, int alto, int ancho){
    for(int i = 0; i < alto; i++)
        for(int j = 0; j < ancho; j++)
            destino[i][j] = original[i][j];
}

// HACE LA PASADA DE PROPAGACIÓN, ACTUALIZA MÁSCARA, AÑADE VECINOS
// Y METE EN LOS VECTORES LA CODIFICACIÓN Y EL CONTEXTO
void propagationPass(Bloque *bloque, int f, int c, int bitplane){
    int CONTEXTOZC = 0;
    CONTEXTOZC = bloque->getContextoZC(f,c);
    bloque->contexto.push_back(CONTEXTOZC);
    if(esSignificativo(bloque->matrix[f][c], bitplane)){
        bloque->mascaraSignificativos[f][c] = true;
        bloque->anyadeVecinos(f,c);
        bloque->bits.push_back(1);
        bloque->contextoSP(f,c);
    }else bloque->bits.push_back(0);
}

// HACE LA PASADA DE REFINAMIENTO, ACTUALIZA LA MÁSCARA
// Y METE EN LOS VECTORES LA CODIFICACIÓN Y EL CONTEXTO
void magnitudeRefinementPass(Bloque *bloque, int f, int c, int bitplane){
    int CONTEXTOMR = 0;
    CONTEXTOMR = bloque->getContextoMR(f,c);
    bloque->contexto.push_back(CONTEXTOMR);
    bloque->bits.push_back(bloque->getBit(f, c, bitplane));
    bloque->contexto.push_back(CONTEXTOMR);
    bloque->mascaraRefinados[f][c] = true;
}

// HACE LA PASADA DE LIMPIEZA, ACTUALIZA LA MÁSCARA Y
// METE EN LOS VECTORES LA CODIFICACIÓN Y EL CONTEXTO
void cleanUpPass(Bloque *bloque, int f, int c, int bitplane){
    int CONTEXTOZC = 0;
    CONTEXTOZC = bloque->getContextoZC(f,c);
    bloque->contexto.push_back(CONTEXTOZC);
    if(esSignificativo(bloque->matrix[f][c], bitplane)){
        bloque->mascaraSignificativos[f][c] = true;
        bloque->anyadeVecinos(f, c);
        bloque->bits.push_back(1);
        bloque->contextoSP(f,c);
    }else bloque->bits.push_back(0);
}


// BUCLE QUE RECORRE EL BLOQUE RECIBIDO Y VA LLAMANDO
// A LAS FUNCIONES QUE HACEN LAS PASADAS, ACTUALIZAN
// MÁSCARAS, ETC.
void hacerPasadas(int **matriz, int ancho, int alto, int sub){

    // Aquí nos llega el bloque con el nombre matriz y su ancho y alto
    int stripeAnterior = 0;

    bool **mascaraCU;   // Esta máscara es necesaria para obtener los coeficientes
                        // no explorados durante una pasada
    mascaraCU = new bool*[alto];
    for (int i = 0 ; i < alto ; i++)
      mascaraCU[i] = new bool[ancho];

    bool **mascaraMR;   // Esta máscara es necesaria para obtener los coeficientes
                        // que eran significativos en pasadas anteriores
    mascaraMR = new bool*[alto];
    for (int i = 0 ; i < alto ; i++)
      mascaraMR[i] = new bool[ancho];

    Bloque *bloque = new Bloque(matriz, alto, ancho, sub);


    //    cout << "Ancho: " << ancho << endl << "Alto: " << alto << endl;

    //    cout << "Ancho: " << bloque->ancho << endl << "Alto: " << bloque->alto << endl;
    /*
    for(int i = 0; i < alto; i++){
        cout << endl;
        for(int j = 0; j < ancho; j++){
            cout << bloque->matrix[i][j] << " ";
        }
    }*/


    int numBitPlanes = getNumBitPlanes(bloque);
    cout << "Numero de bit planes: " << numBitPlanes << endl << endl << endl;

    while(numBitPlanes > 0){
        numBitPlanes--; //Empieza por el bitplane 4
        //cout << "Bit plane " << numBitPlanes << endl;
        int contSPBitPlane = 0, contMRBitPlane = 0, contCUBitPlane = 0;
        stripeAnterior = 0;
        while(stripeAnterior < alto){
            copiarMatriz(mascaraCU, bloque->mascaraVecinos, alto, ancho);
            copiarMatriz(mascaraMR, bloque->mascaraSignificativos, alto, ancho);
            for (int c = 0; c < ancho; c++){
                for (int f = stripeAnterior; f < (stripeAnterior+4) && f < alto; f++){
                    if(bloque->mascaraVecinos[f][c] && !bloque->mascaraSignificativos[f][c]){
                        propagationPass(bloque, f, c, numBitPlanes);
                        contSPBitPlane++;
                    }
                }
            }

            for (int c = 0; c < ancho; c++){
                for (int f = stripeAnterior; f < (stripeAnterior+4) && f < alto; f++){
                    if(mascaraMR[f][c]){
                        magnitudeRefinementPass(bloque, f, c, numBitPlanes);
                        contMRBitPlane++;
                    }
                }
            }
            for (int c = 0; c < ancho; c++){
                for (int f = stripeAnterior; f < (stripeAnterior+4) && f < alto; f++){
                    if(!mascaraCU[f][c] && !bloque->mascaraSignificativos[f][c]){
                        cleanUpPass(bloque, f, c, numBitPlanes);
                        contCUBitPlane++;
                    }
                }
            }

            stripeAnterior += 4;
        }
        cout << "-------------------------------------------------" << endl;
        cout << "| PLANO | PASADA        | NUMERO DE COEFICIENTES|" << endl;
        cout << "|       | Propagacion   | " << contSPBitPlane << "\t\t\t|" << endl;
        cout << "|   " << numBitPlanes << "   | Refinamiento  | " << contMRBitPlane << "\t\t\t|" << endl;
        cout << "|       | Clean-up      | " << contCUBitPlane << "\t\t\t|" << endl;
    }

    // IMPRIME LOS VECTORES RESULTADO

/*
    vector<int>::size_type szBits = bloque->bits.size();
    vector<int>::size_type szContexto = bloque->contexto.size();

    cout << "Vector \"bits\" contiene: ";
    for (int i=0; i<szBits; i++)
      cout << " " << bloque->bits[i];
    cout << endl << endl;
    cout << "Vector \"contexto\" contiene: ";
    for (int i=0; i<szContexto; i++)
      cout << " " << bloque->contexto[i];
*/

}





int main(int argc, char **argv)
{
    printf("\n--------------------------------\n");
    printf("PROYECTO REDES MULTIMEDIA (RMM)\n");
    printf("Facultad de Informatica, UPV\n");
    printf("--------------------------------\n");
    printf("Proyecto de Codificador EBCOT\n");
    printf("--------------------------------\n\n\n");



    int **BloqueEj;
    int ancho, alto, nivel, sub;
    ReservaPlano(64,64,&BloqueEj);
    char fichero[] = "lena.bl1";
    CargaBloque("lena.bl1", &ancho, &alto, &nivel, &sub, BloqueEj);

    //cout << "Ancho: " << ancho << endl << "Alto: " << alto << endl;
    //cout << "Nivel: " << nivel << endl << "Subbanda: " << sub << endl;

    // IMPRIME EL BLOQUE
    /*
    for(int i = 0; i < alto; i++){
        cout << endl;
        for(int j = 0; j < ancho; j++){
            cout << BloqueEj[i][j] << " ";
        }
    }*/
    hacerPasadas(BloqueEj, ancho, alto, sub);


    return 0;

}
















